#include <cstring>
#include <cmath>
#include <cstdio>
#include <nds.h>
#include "decoder_wave.h"
#include "decoder.h"
#include "debug.h"

// ======================================
// Methods for Playlist
// ======================================

// ----------------
bool DecoderWAVE::checkFormat(std::string filepath)
// ----------------
{
	// init RIFF class on slot 1
	if (!riff.init(filepath))
	{
		// init failed, not a RIFF file
		return false;
	}
	// check if the type is WAVE
	ContainerRIFFChunk chHead = riff.getChunkHead();
	if (chHead.ckType.compare(0, 4, "WAVE") != 0) return false;
	
	return true;
}

// ----------------
fileInfo DecoderWAVE::getInfo()
// ----------------
{
	fileInfo info;
	ContainerRIFFChunk chHead;
	ContainerRIFFChunk schHead;
	char dataBuffer[16];
	uint16_t tmpInt = 0;
	
	// parse chunks
	while ( riff.nextChunk() )
	{
		// check chunk type
		chHead = riff.getChunkHead();

		#ifdef DEBUG_DEC_WAVE
		iprintf("chHead: '%s'\n", chHead.ckID.data());
		#endif
		
		if (chHead.ckID.compare(0, 4, "fmt ") == 0)
		{
			#ifdef DEBUG_DEC_WAVE
			iprintf("test\n");
			#endif

			// read data
			riff.readChunkData(dataBuffer, 16);

			#ifdef DEBUG_DEC_WAVE
			iprintf("test\n");
			iprintf("wFormatTag: %u\n", *((uint16_t *)&dataBuffer[0]));
			#endif

			// wFormatTag
			if ((*(uint16_t *)(&dataBuffer[0])) == WAVE_FORMAT_PCM)
			{
				info.type.assign("WAVE");
				is_pcm = true;
			}
			else
			{
				// only PCM is supported
				is_pcm = false;
				info.type.assign("UNSP");
				break;
			}
			
			// nChannels
			info.channels = (uint8_t)(*(uint16_t *)(&dataBuffer[2]));
			#ifdef DEBUG_DEC_WAVE
			iprintf("Ch: %d\n", info.channels);
			#endif
			
			// nSamplesPerSec
			info.sampleRate = *(uint32_t *)(&dataBuffer[4]);
			#ifdef DEBUG_DEC_WAVE
			iprintf("SR: %d\n", info.sampleRate);
			#endif
			
			// nAvgBytesPerSec
			info.kbps = (*(uint32_t *)(&dataBuffer[8])) >> 7;
			#ifdef DEBUG_DEC_WAVE
			iprintf("k: %d\n", info.kbps);
			#endif
			
			// nBlockAlign
			tmpInt = *(uint16_t *)(&dataBuffer[12]);
			#ifdef DEBUG_DEC_WAVE
			iprintf("BA: %d\n", tmpInt);
			#endif
			
			// wBitsPerSample
			info.bits = (uint8_t)(*(uint16_t *)(&dataBuffer[14]));
			#ifdef DEBUG_DEC_WAVE
			iprintf("b: %d\n", info.bits);
			#endif
		}
		else if (chHead.ckID.compare(0, 4, "LIST") == 0 && chHead.ckType.compare(0, 4, "INFO") == 0)
		{
			char name[256];
			while ( riff.nextSubChunk() )
			{
				// check subchunk type
				schHead = riff.getSubChunkHead();
				
				if ( schHead.ckID.compare(0, 4, "INAM") == 0 )
				{
					riff.readSubChunkData(name, schHead.ckSize);
					info.title.assign( name );
				}
				else if ( schHead.ckID.compare(0, 4, "IART") == 0 )
				{
					riff.readSubChunkData(name, schHead.ckSize);
					info.artist.assign( name );
				}
				else if ( schHead.ckID.compare(0, 4, "ICRD") == 0 )
				{
					riff.readSubChunkData(name, schHead.ckSize);
					info.year.assign( name );
				}
				//else if ( schHead.ckID.compare(0, 4, "ICMT") == 0 ) // comment
				//{
				//	
				//}
			}
		}
		else if (chHead.ckID.compare(0, 4, "data")  == 0)
		{
			info.length = chHead.ckSize / tmpInt;
			#ifdef DEBUG_DEC_WAVE
			iprintf("'data' length=%u\n", info.length);
			#endif
			break;
		}
	}
	
	info.album.clear();
	info.track.clear();
	info.alltracks.clear();
	info.rgLoudness = 0;
	info.rgTrackGain = 0;
	info.rgTrackPeak = 0;
	info.rgAlbumGain = 0;
	info.rgAlbumPeak = 0;
	
	return info;
}

// ======================================
// Methods for Player
// ======================================

// ----------------
void DecoderWAVE::prprForPlayback(fileInfo info)
// ----------------
{
	bits = info.bits;
	channels = info.channels;
	riff.init(info.filepath);
}

// ----------------
void DecoderWAVE::toFirstSample()
// ----------------
{
	// rewind the RIFF
	riff.rewind();
	ContainerRIFFChunk chHead;
	
	while ( riff.nextChunk() )
	{
		// search for data chunk
		chHead = riff.getChunkHead();
		
		if (chHead.ckID.compare(0, 4, "data") == 0)
		{
			break;
		}
	}
	frame = 0;
}

// ----------------
void DecoderWAVE::fillBuffers(int16_t *buffer_l, int16_t *buffer_r, uint32_t samples)
// ----------------
{
	// read data
	char data[samples*channels*(bits>>3)];
	riff.readChunkData(data, samples*channels*(bits>>3));

	if (bits == 16)
	{
		// create a pointer so we could refer to the data as table of s16
		int16_t *tempPointer = (int16_t *)data;
		
		// copy the samples
		for (uint32_t i=0; i < samples; ++i)
		{
			buffer_l[i] = tempPointer[(i*channels)];
			
			if (channels > 1)
			{
				// stereo or more but we have 2 speakers
				buffer_r[i] = tempPointer[(i*channels + 1)];
			}
			else
			{
				// mono
				buffer_r[i] = buffer_l[i];
			}
			
			// increase the frame count
			++frame;
		}
	}
	else if (bits == 8)
	{
		// create a pointer so we could refer to the data as table of u8
		uint8_t *tempPointer = (uint8_t *)data;
		
		// copy the samples
		for (uint32_t i=0; i < samples; ++i) {
			//            v -------- upscale to s16 ---------- v
			buffer_l[i] = (tempPointer[(i*channels)] - 128) << 8;
			
			if (channels > 1)
			{
				// stereo or more but we have 2 speakers
				buffer_r[i] = (tempPointer[(i*channels + 1)] - 128) << 8;
			}
			else
			{
				// mono
				buffer_r[i] = buffer_l[i];
			}
			++frame;
		}
	}
}

// ----------------
uint32_t DecoderWAVE::getCurrentFrame() {
// ----------------
	return frame;
}

// ----------------
bool DecoderWAVE::reachedEnd()
// ----------------
{
	return (riff.getChunkRemLen() < (int)((bits>>3)*channels));
}

